home *** CD-ROM | disk | FTP | other *** search
/ Garbo / Garbo.cdr / mac / comm / revrdist.sit / RevRdist / RevRdist src / distfile.c < prev    next >
Encoding:
C/C++ Source or Header  |  1990-06-28  |  18.8 KB  |  851 lines  |  [TEXT/KAHL]

  1. /*
  2.  * distfile.c - routines for reading/writing the distribution control file
  3.  */
  4.  
  5. #include "RevRdist.h"
  6. #include "dispatch.h"
  7. #include <string.h>
  8. #include <TransSkelProto.h>
  9. #include <TransDisplayProto.h>
  10.  
  11. /*
  12.  * iob - the structure used when reading the control file.
  13.  * we do the I/O ourselves rather than include all of the stdio library.
  14.  */
  15.  
  16. struct iob
  17. {
  18.     int            size;                /* the size of the buffer */
  19.     int            cnt;                /* number of bytes in the buffer */
  20.     int            idx;                /* index of next byte in buffer */
  21.     Integer        refNum;                /* file reference number */
  22.     Byte        buf[1];                /* I/O buffer */
  23. };
  24.  
  25.  
  26. static    OSErr getDActions ();
  27.         OSErr getDActions (StringPtr, actions_t *);
  28. static    OSErr getDField ();
  29.         OSErr getDField (StringPtr, int *, int, StringPtr);
  30. static    OSErr getDType ();
  31.         OSErr getDType (tnode_t *, StringPtr);
  32. static    OSErr openDistFile ();
  33.         OSErr openDistFile (struct iob *);
  34. static    OSErr readDLine ();
  35.         OSErr readDLine (struct iob *, StringPtr);
  36. static    void skipSpaces ();
  37.         void skipSpaces (StringPtr, int *);
  38.  
  39.  
  40. /*
  41.  *=========================================================================
  42.  * freeDist (node) - free dist_node tree
  43.  * entry:    node = ptr to root of tree
  44.  *=========================================================================
  45.  */
  46. void
  47. freeDist (node)
  48.     dnode_t        *node;
  49. {
  50.     dnode_t        *parent, *child, *prev, *next;
  51.     tnode_t        *tp, *pp, *np;
  52.  
  53.     if (node == nil)
  54.         return;
  55.     /*
  56.      * First, unlink this node from any sibling chain
  57.      * (If our parent's child pointer is nil, skip this.)
  58.      */
  59.     if ((parent = node->parentp))
  60.     {
  61.         if ((prev = parent->childp))
  62.         {
  63.             if (prev == node)
  64.                 /* node is first child */
  65.                 parent->childp = node->sibp;
  66.             else
  67.                 /* scan sib list for node */
  68.                 for (next = prev->sibp; next; prev = next, next = next->sibp)
  69.                     if (next == node)
  70.                     {
  71.                         prev->sibp = node->sibp;
  72.                         break;
  73.                     }
  74.             node->sibp = nil;
  75.         }
  76.     }
  77.     /*
  78.      * Next, recursively free any children
  79.      */
  80.     for (next = node->childp, node->childp = nil; next; )
  81.     {
  82.         prev = next;
  83.         next = next->sibp;
  84.         freeDist (prev);
  85.     }
  86.     /*
  87.      * Free any type_nodes back to parent's
  88.      */
  89.     pp = parent ? parent->tlistp : nil;
  90.     for (tp = node->tlistp; tp && tp != pp; tp = np)
  91.     {
  92.         np = tp->morep;
  93.         DisposPtr ((Ptr) tp);
  94.     }
  95.     /*
  96.      * Finally, free selves
  97.      */
  98.     if (node->altname)
  99.         DisposPtr ((Ptr) node->altname);
  100.     DisposPtr ((Ptr) node);
  101. }
  102.  
  103.  
  104.  
  105. /*
  106.  *=========================================================================
  107.  * parseDistFile () - build a dist_node tree from the control file
  108.  * entry:    This is a dispatcher routine.
  109.  *            The calling argument pointer is ignored.
  110.  * returns:    nil on failure, reason in ClueID, etc
  111.  *            ptr to dist_node tree if file read correctly.
  112.  *
  113.  * There are five formats for lines in the control file:
  114.  * - Comments, which begin with #
  115.  * - Folder starts, which begin with > and look like
  116.  *        > Foldername :Folderactions [:Alternate name]
  117.  * - Folder ends, which begin with < and look like
  118.  *        < Foldername :DefaultActionsForFolder
  119.  * - Files, which begin with | and look like
  120.  *        | Filename :Fileactions [:Alternate name]
  121.  * - Type/creator specifications, which begin with * and look like
  122.  *        * Type :Actions
  123.  *        * (Creator) :Actions
  124.  *     or    * Type(Creator) :Actions
  125.  *     where Type is a four character file type and Creator is a four
  126.  *     character file creator signature.
  127.  *=========================================================================
  128.  */
  129.  
  130. DISPATCHED (parseDistFile)
  131. {
  132.     struct    lm                        /* local memory */
  133.     {
  134.         frame_t            f;
  135.         struct iob *    iob;
  136.         int                ecnt;        /* error count */
  137.         int                lcnt;        /* control file line counter */
  138.         dnode_t *        head;        /* ptr to base of tree */
  139.         dnode_t *        parent;        /* parent node */
  140.         dnode_t *        prev;        /* previous node in sibling chain */
  141.         dnode_t *        next;        /* next node in sibling chain */
  142.     };
  143.     typedef struct lm    lm_t;
  144. register lm_t            *m;            /* ptr to local memory */
  145.     OSErr                error;        /* serious error number */
  146.     int                    idx;        /* index into lineBuf */
  147. register dnode_t *        node;        /* working node */
  148.     int                    len;        /* length of current line */
  149.     Ptr                    p;            /* temp pointer */
  150.     Ptr                    paramv[2];    /* args/return values */
  151.     int                    sel;        /* type of control file entry */
  152.     StringPtr            str;        /* string temp */
  153.     tnode_t *            tp;            /* current type_node */
  154.     actions_t            actions;    /* action list for current node */
  155.     Str255                lineBuf;    /* buffer for line from control file */
  156.     Str255                nBuf;        /* current component name */
  157.     Str255                tBuf;        /* temp buffer */
  158.  
  159.     error = 0;
  160.     m = *(lm_t **)fh;
  161.     switch (request)
  162.     {
  163.     case R_INIT:
  164.         /*
  165.          * Initial call: just see if we can get the memory we will need
  166.          * and open the control file.
  167.          */
  168.         if (error = resizeFrame (fh, sizeof (lm_t)))
  169.             break;
  170.         p = NewPtr (sizeof (struct iob) + 512 - 1);
  171.         HLock ((Handle)fh);
  172.         m = *(lm_t **)fh;
  173.         m->iob = (struct iob *) p;
  174.         if (p == nil)
  175.         {
  176.             Clue1 = (SP) "\pNewPtr";
  177.             error = MemError();
  178.             break;
  179.         }
  180.         m->iob->size = 512;
  181.         m->iob->cnt = m->iob->idx = 0;
  182.         m->iob->refNum = 0;
  183.         error = openDistFile (m->iob);
  184.         if (error)
  185.             break;
  186.  
  187.         m->f.state = 1;
  188.         HUnlock ((Handle)fh);
  189.         return R_CONT;
  190.  
  191.     case R_CONT:
  192.         /*
  193.          * We have only one "continue" state, and in that state we
  194.          * process one line from the control file per call
  195.          */
  196.         if (m->f.state != 1)
  197.         {
  198.             Clue0 = (SP) "\pparseDistFile";
  199.             NumToString ((long)m->f.state, Mbuf);
  200.             panic (true, E_STATE, NullStr, Mbuf, nil);
  201.             return R_QUIT;
  202.         }
  203.             
  204.         error = readDLine (m->iob, lineBuf);
  205.         if (error)
  206.             break;
  207.         m->lcnt++;
  208.         if (Flags & DB_ECHODIST)
  209.         {
  210.             DisplayInt (m->lcnt);
  211.             DisplayString (lineBuf);
  212.             DisplayLn ();
  213.         }
  214.         len = lineBuf[0];
  215.         idx = 1;
  216.         skipSpaces (lineBuf, &idx);
  217.         /*
  218.          * skip empty lines and lines beginning with #
  219.          */
  220.         if (idx > len || lineBuf[idx] == '#')
  221.             break;
  222.         /*
  223.          * see which type of line we have
  224.          */
  225.         sel = lineBuf[idx++];
  226.         if (sel != '<' && sel != '|' && sel != '>' && sel != '*')
  227.         {
  228.             error = E_PREFIX;
  229. synerr:
  230.             m->ecnt++;
  231.             GetIndString (tBuf, ERR_STR, error);
  232.             DisplayString (tBuf);
  233.             DisplayInt (m->lcnt);
  234.             DISPLAY ("\p : ");
  235.             DisplayString (lineBuf);
  236.             DISPLAY ("\p\r");
  237.             error = 0;
  238.             break;
  239.         }
  240.         /*
  241.          * extract the file/folder name or type/creator
  242.          */
  243.         if (getDField (lineBuf, &idx, ':', nBuf))
  244.         {
  245.             error = E_NOFNAME;
  246.             goto synerr;
  247.         }
  248.         if (sel != '*' && strchr ((char *)nBuf+1, ':'))
  249.         {
  250.             error = E_BADNAME;
  251.             goto synerr;
  252.         }
  253.         /*
  254.          * extract and interpret the actions list
  255.          */
  256.         if (getDField (lineBuf, &idx, ' ', tBuf))
  257.         {
  258.             error = E_NOACTION;
  259.             goto synerr;
  260.         }
  261.         if (getDActions (tBuf, &actions))
  262.         {
  263.             error = E_BADACTION;
  264.             goto synerr;
  265.         }
  266.         /*
  267.          * extract the server alternate name field, if present
  268.          */
  269.         if (getDField (lineBuf, &idx, 0x100, tBuf) == 0)
  270.         {
  271.             if (tBuf[0] < 2 || tBuf[1] != '=')
  272.             {
  273.                 error = E_ALTNAME;
  274.                 goto synerr;
  275.             }
  276.         }
  277.         /*
  278.          * Now that we've parsed the line into its pieces, use the
  279.          * pieces to fill in its dist_node
  280.          */
  281.         switch (sel)
  282.         {
  283.         case '<':
  284.             /* 
  285.              * end of folder contents
  286.              * verify that there is a folder for us to match,
  287.              * then copy actions to folder default entry
  288.              * and unnest a level
  289.              */
  290.             if (m->parent == nil)
  291.             {
  292.                 error = E_LAST;
  293.                 break;
  294.             }
  295.             if ((node = m->parent->childp) == nil
  296.             ||  node->d_type != D_FOLDERDEF
  297.             ||  node->name[0] != 0
  298.             )
  299.             {
  300.                 error = E_ENDFERR;
  301.                 goto emexit;
  302.             }
  303.             node->actions = actions;
  304.             node = node->parentp;
  305.             m->parent = node->parentp;
  306.             m->prev = m->next = node;
  307.             break;
  308.  
  309.         case '|':
  310.             if (m->head == nil)
  311.             {
  312.                 error = E_FIRST;
  313.                 break;
  314.             }
  315.             /* fall into ... */
  316.         case '>':
  317.             /*
  318.              * for files and folders, create a new node, fill it in,
  319.              * and link it in alphabetically
  320.              */
  321.             node = (dnode_t *) NewPtr (sizeof (dnode_t));
  322.             if (node == nil)
  323.             {
  324.                 error = MemError();
  325.                 break;
  326.             }
  327.             ZEROAT(node);
  328.             node->parentp = m->parent;
  329.             node->d_type = sel == '|' ? D_FILE : D_FOLDER;
  330.             node->actions = actions;
  331.             COPYPS (nBuf, node->name);
  332.             if (m->head == nil)
  333.             {
  334.                 m->head = node;
  335.                 m->prev = m->next = node;
  336.             }
  337.             else
  338.             {
  339.                 if (m->parent == nil || m->prev == nil)
  340.                 {
  341.                     DisposPtr ((Ptr) node);
  342.                     error = E_DISTBAD;
  343. emexit:
  344.                     /*
  345.                      * Here for error where showing the bad control file line
  346.                      * makes sense
  347.                      */
  348.                     COPYPS (lineBuf, Mbuf);
  349.                     Clue1 = Mbuf;
  350.                     break;
  351.                 }
  352.                 /*
  353.                  * insert alphabetically in sibling chain
  354.                  */
  355.                 if (RelString (nBuf, m->prev->name, false, true) < 0)
  356.                     m->prev = m->next = m->parent->childp;
  357.                 for (; m->next; m->prev = m->next, m->next = m->next->sibp)
  358.                     if (RelString (nBuf, m->next->name, false, true) < 0)
  359.                         break;
  360.                 node->sibp = m->next;
  361.                 m->prev->sibp = node;
  362.                 m->prev = node;
  363.             }
  364.             /*
  365.              * If there is an alternate name, copy it to alloc'ed space
  366.              * (except for the leading = )
  367.              */
  368.             if ((len = tBuf[0]))
  369.             {
  370.                 if ((node->altname = (StringPtr) NewPtr (len)) == nil)
  371.                 {
  372.                     if (!(error = MemError()))
  373.                         error = memFullErr;
  374.                     break;
  375.                 }
  376.                 BlockMove (tBuf+1, node->altname, (long) len);
  377.                 node->altname[0] = len - 1;
  378.             }
  379.             /*
  380.              * If a file, done.
  381.              */
  382.             if (sel == '|')
  383.                 break;
  384.             /*
  385.              * Propagate our parent's type_node list
  386.              */
  387.             if (node->parentp)
  388.                 node->tlistp = node->parentp->tlistp;
  389.             /*
  390.              * Allocate a dummy child which will hold the folder defaults
  391.              */
  392.             m->next = (dnode_t *) NewPtr (sizeof (dnode_t));
  393.             if (m->next == nil)
  394.             {
  395.                 if (!(error = MemError()))
  396.                     error = memFullErr;
  397.                 break;
  398.             }
  399.             ZEROAT(m->next);
  400.             m->next->parentp = node;
  401.             m->next->d_type = D_FOLDERDEF;
  402.             node->childp = m->next;
  403.             m->parent = node;
  404.             m->prev = m->next;
  405.             break;
  406.  
  407.         case '*':
  408.             /*
  409.              * File type/creator selection.
  410.              * Allocate a type_list node and link it in to the current
  411.              * folder dist_node.
  412.              */
  413.             node = m->parent;
  414.             if (node == nil)
  415.             {
  416.                 error = E_FIRST;
  417.                 break;
  418.             }
  419.             tp = (tnode_t *) NewPtr (sizeof (tnode_t));
  420.             if (tp == nil)
  421.             {
  422.                 error = memFullErr;
  423.                 break;
  424.             }
  425.             ZEROAT (tp);
  426.             error = getDType (tp, nBuf);    /* fill in type/creator */
  427.             if (error)
  428.             {
  429.                 DisposPtr ((Ptr) tp);
  430.                 break;
  431.             }
  432.             tp->actions = actions;            /* fill in actions */
  433.             tp->morep = node->tlistp;
  434.             node->tlistp = tp;
  435.             break;
  436.         }
  437.         break;
  438.  
  439.     case R_BACKOUT:
  440.     case R_QUIT:
  441.         error = E_QUIT;                /* magic error number */
  442.         break;
  443.  
  444.     default:
  445.         error = E_REQUEST;            /* cannot happen */
  446.  
  447.     }
  448.     /*
  449.      * If this call had a serious error or if the end of the file was
  450.      * reached and there were previous minor (syntax) errors, clean up
  451.      * and return an error to the caller.
  452.      * Otherwise, pop back to caller, returning the head of the tree we built.
  453.      * In any case, close the file and return the iob resources.
  454.      */
  455.     if (error == 0)
  456.     {
  457.         /*
  458.          * if error is not set, we are not done.
  459.          */
  460.         return R_CONT;
  461.     }
  462.     if (GetHandleSize ((Handle)fh) < sizeof (lm_t))
  463.         return (popCall (E_QUIT, nil));
  464.     /*
  465.      * EOF error is not a real error.  It is how we know we are done.
  466.      */
  467.     if (error == eofErr)
  468.     {
  469.         error = 0;
  470.         /*
  471.          * Check that start and end folders balanced out
  472.          */
  473.         if (m->parent)
  474.             error = E_LAST;
  475.     }
  476.     if (error == 0 && m->ecnt)
  477.         error = E_DISTL;
  478.     if (m->iob)
  479.     {
  480.         file_info_t *    fi;
  481.         
  482.         fi = &File_list[FL_DIST];
  483.         if (m->iob->refNum)
  484.         {
  485.             (void) FSClose (m->iob->refNum);
  486.             if (m->iob->refNum == fi->f_ref)
  487.                 fi->f_ref = 0;
  488.         }
  489.         DisposPtr ((Ptr) m->iob);
  490.         m->iob = 0;
  491.     }
  492.     if (error)
  493.     {
  494.         ClueID = error;
  495.         Clue0 = (SP) "\pparseDistFile";
  496.         freeDist (m->head);
  497.         m->head = 0;
  498.     }
  499.     paramv[0] = (Ptr) m->head;
  500.     return (popCall (request==R_INIT ? R_CONT : request, paramv));
  501. }
  502.  
  503.  
  504. /*
  505.  *=========================================================================
  506.  * getDActions (str, ap) - decode action list
  507.  * entry:    str = string with action list text
  508.  *            ap = ptr to action structure to set from action list
  509.  * returns:    0 if no error, else non-zero
  510.  *=========================================================================
  511.  */
  512. static
  513. OSErr
  514. getDActions (str, ap)
  515. register StringPtr        str;
  516. register actions_t *    ap;
  517. {
  518. register int            c;
  519. register int            i;
  520. register int            len;
  521.     int                    opt;
  522.  
  523.     setmem ((char *) ap, sizeof (*ap), A_DEFAULT);
  524.     for (i = 0, len = str[0]; i+1 < len; )
  525.     {
  526.         c = str[++i];                /* letter for which condition */
  527.         if (c >= 'a' && c <= 'z')
  528.             c = c - 'a' + 'A';
  529.         opt = c;
  530.         c = str[++i];                /* letter for which action */
  531.         if (c >= 'A' && c <= 'Z')
  532.             c = c - 'A' + 'a';
  533.         switch (c)
  534.         {
  535.         case '.':    c = A_DEFAULT;    break;
  536.         case '-':    c = A_PASS;        break;
  537.         case 'i':    c = A_IGNORE;    break;
  538.         case 'j':    if (opt == 'H' || opt == 'L')
  539.                         c = A_IGNORE;
  540.                     else
  541.                         c = A_JUNK;
  542.                     break;
  543.         case 'd':    c = A_DISCARD;    break;
  544.         case 'u':    c = A_UPDATE;    break;
  545.         case 's':    if (opt == 'H' || opt == 'L')
  546.                     {
  547.                     c = A_JUNK;        break;
  548.                     }
  549.                     /* else fall into */
  550.         default:    return E_BADACTION;
  551.         }
  552.         switch (opt)
  553.         {
  554.         case 'C':    ap->ifclient = c;    break;
  555.         case 'S':    ap->ifserver = c;    break;
  556.         case 'V':    ap->ifcreate = c;    break;
  557.         case 'N':    ap->ifnewer = c;    break;
  558.         case 'O':    ap->ifolder = c;    break;
  559.         case 'Z':    ap->ifsize = c;        break;
  560.         case 'E':    ap->otherwise = c;    break;
  561.         case 'W':    ap->copywindow = c;    break;
  562.         case 'H':    ap->invisible = c;    break;
  563.         case 'L':    ap->locked = c;        break;
  564.         case 'A':    ap->ifclient = c;
  565.                     ap->ifserver = c;
  566.                     ap->ifcreate = c;
  567.                     ap->ifnewer = c;
  568.                     ap->ifolder = c;
  569.                     ap->ifsize = c;
  570.                     ap->otherwise = c;
  571.                     ap->copywindow = c;
  572.                     ap->invisible = c;
  573.                     ap->locked = c;
  574.                     break;
  575.         default:    return E_BADACTION;
  576.         }
  577.     }
  578.     return 0;
  579. }
  580.  
  581.  
  582.  
  583. /*
  584.  *=========================================================================
  585.  * getDField (src, idx, term, dst) - extract field from line
  586.  *                where a field is delimited by term character or end of line
  587.  * entry:    src = ptr to line
  588.  *            idx = ptr to current index into src line
  589.  *            term = character to use to terminate field
  590.  *                special values:
  591.  *                    ' ' -> terminate on whitespace
  592.  *                    ':' -> terminate on ':' and also strip trailing whitespace
  593.  *            dst = ptr to string to receive field
  594.  * returns:    0 if field found, idx updated
  595.  *            <> 0 if no field remaining in line
  596.  *=========================================================================
  597.  */
  598. static
  599. int
  600. getDField (src, idx, term, dst)
  601. register    StringPtr    src;
  602.             int    *        idx;
  603.             int            term;
  604. register    StringPtr    dst;
  605. {
  606.     register    int        len;
  607.     register    int        i, j;
  608.     register    int        c;
  609.                 int        k;            /* tracks last non-whitespace */
  610.  
  611.     skipSpaces (src, idx);            /* skip leading spaces */
  612.     len = src[0];
  613.     i = *idx;
  614.     j = 0;
  615.     k = j;
  616.     dst[j] = 0;
  617.     if (i > len)
  618.         return -1;                    /* no field data present */
  619.     while (i <= len)
  620.     {
  621.         c = src[i++];
  622.         if (c == '\\' && i <= len)    /* allow \ to quote chars */
  623.         {
  624.             c = src[i++];
  625.             k = j + 1;
  626.         }
  627.         else
  628.         {
  629.             if (c == term)
  630.                 break;
  631.             if (term == ' ' && c == '\t')
  632.                 break;
  633.         }
  634.         dst[++j] = c;
  635.         if (c != ' ' && c != '\t')
  636.             k = j;
  637.     }
  638.     *idx = i;
  639.     if (c == ':')
  640.         j = k;
  641.     dst[j+1] = 0;
  642.     dst[0] = j;
  643.     return 0;
  644. }
  645.  
  646.  
  647.  
  648. /*
  649.  *=========================================================================
  650.  * getDType (tp, sp) - Parse TYPE and CREATOR string
  651.  * entry:        tp = ptr to type_node to fill in
  652.  *                sp = ptr to string with type and creator
  653.  * returns:        0 if no error, else error number
  654.  *=========================================================================
  655.  */
  656.  
  657. OSErr
  658. getDType (tp, sp)
  659. register tnode_t *    tp;
  660. register StringPtr    sp;
  661. {
  662. register    int        len;            /* chars in sp */
  663.             OSErr    error;
  664.  
  665. #define    LT    sizeof(tp->ftype)        /* length of a file type */
  666. #define    LC    sizeof(tp->fcreator)    /* length of a file creator */
  667.  
  668.     error = E_BADTYPE;                /* assume failure */
  669.     len = sp[0];                    /* string length */
  670.     switch (len)
  671.     {
  672.     case LT:                        /* just TYPE */
  673.         BlockMove (sp+1, (Ptr)&tp->ftype, LT);
  674.         error = 0;
  675.         break;
  676.     
  677.     case 1+LC+1:                    /* just (CREATOR) */
  678.         if (sp[1] == '(' && sp[len] == ')')
  679.         {
  680.             BlockMove (sp+2, (Ptr)&tp->fcreator, LC);
  681.             error = 0;
  682.         }
  683.         break;
  684.         
  685.     case LT+1+LC+1:                    /* TYPE(CREATOR) */
  686.         if (sp[LT+1] == '(' && sp[len] == ')')
  687.         {
  688.             BlockMove (sp+1, (Ptr)&tp->ftype, LT);
  689.             BlockMove (sp+1+LT+1, (Ptr)&tp->fcreator, LC);
  690.             error = 0;
  691.         }
  692.         break;
  693.     }
  694.     return error;
  695. }
  696.  
  697.  
  698.  
  699. /*
  700.  *=========================================================================
  701.  * openDistFile (iob) - locate and open the control file
  702.  * entry:    iob = pointer to iob structure
  703.  *            Ap_file global names the control file
  704.  * exit:    returns 0 if file opened,
  705.  *            else error number
  706.  *=========================================================================
  707.  */
  708. static
  709. OSErr
  710. openDistFile (iob)
  711. struct iob *    iob;
  712. {
  713.     OSErr        error;
  714.     file_info_t *    fi;
  715.     Integer            ref;
  716.     StringHandle    sh;
  717.     HParamBlockRec    pb;
  718.  
  719.     iob->cnt = 0;
  720.     iob->idx = 0;
  721.     fi = &File_list[FL_DIST];
  722.     if (ref = fi->f_ref)
  723.     {
  724.         iob->refNum = ref;
  725.         return 0;
  726.     }
  727.     sh = fi->f_path;
  728.     HLock ((Handle) sh);
  729.     ZERO (pb);
  730.     pb.fileParam.ioNamePtr = *sh;
  731.     pb.fileParam.ioVRefNum = fi->f_vol;
  732.     pb.ioParam.ioPermssn = fsRdPerm;
  733.     error = PBHOpen (&pb, false);
  734.     if (error == 0)
  735.         ref = pb.ioParam.ioRefNum;
  736.     else
  737.     {
  738.         Clue0 = (SP) "\popenDistFile";
  739.         Clue1 = (SP) "\pFSOpen";
  740.         Clue2 = *sh;
  741.         ref = 0;
  742.     }
  743.     fi->f_ref = iob->refNum = ref;
  744.     return error;
  745. }
  746.  
  747.  
  748. /*
  749.  *=========================================================================
  750.  * readDLine (iob, str) - read line from file into string
  751.  * entry    iob = ptr to I/O control structure
  752.  *            str = string to receive line
  753.  *=========================================================================
  754.  */
  755. static
  756. OSErr
  757. readDLine (iob, str)
  758. register struct iob *    iob;
  759. register StringPtr        str;
  760. {
  761. register int        c;                /* current character */
  762.     Longint            count;            /* bytes read */
  763.     OSErr            error;
  764. register int        len;            /* valid bytes in I/O buffer */
  765. register int        i, j;            /* indexes into buffer, str */
  766.  
  767.     error = 0;
  768.     len = iob->cnt;
  769.     i = iob->idx;
  770.     j = 0;
  771.     c = 0;                            /* set not in quote character */
  772.     for (;;)
  773.     {
  774.         if (i >= len)
  775.         {
  776.             /*
  777.              * if buffer exhausted, read some more
  778.              */
  779.             count = iob->size;
  780.             error = FSRead (iob->refNum, &count, iob->buf);
  781.             iob->cnt = len = count;
  782.             i = 0;
  783.             if (error == eofErr)
  784.                 error = 0;
  785.             if (error)
  786.             {
  787.                 ClueID = error;
  788.                 Clue0 = (SP) "\preadDLine";
  789.                 Clue1 = (SP) "\pFSRead";
  790.                 break;
  791.             }
  792.             if (len <= 0)
  793.             {
  794.                 if (j == 0)
  795.                     error = eofErr;    /* if nothing left, return eof */
  796.                 break;                /* if didn't get anything, quit */
  797.             }
  798.             continue;
  799.         }
  800.         if (c == -1)
  801.             str[++j] = c = iob->buf[i++];    /* copy quoted char exactly */
  802.         else
  803.         {
  804.             c = iob->buf[i++];
  805.             if (c == '\n' || c == '\r')
  806.                 break;                    /* exit on end of line */
  807.             str[++j] = c;
  808.             if (c == '\\')
  809.                 c = -1;                    /* flag next char quoted */
  810.         }
  811.         if (j >= 255)
  812.             break;
  813.     }
  814.     iob->idx = i;
  815.     str[0] = j;
  816.     if (j < 255)
  817.         str[j+1] = 0;
  818.     return error;
  819. }
  820.  
  821.  
  822.  
  823. /*
  824.  *=========================================================================
  825.  * skipSpaces (src, idx) - skip leading spaces in string
  826.  * entry:    src = ptr to string
  827.  *            idx = ptr to current index in string
  828.  * exit        idx advanced past any leading whitespace in string
  829.  *=========================================================================
  830.  */
  831. static
  832. void
  833. skipSpaces (src, idx)
  834. register StringPtr    src;
  835. register int *        idx;
  836. {
  837. register int        len;
  838. register int        c;
  839. register int        i;
  840.  
  841.     len = src[0];
  842.     i = *idx;
  843.     while (i <= len)
  844.     {
  845.         c = src[i];
  846.         if (c != ' ' && c != '\t')
  847.             break;
  848.         ++i;
  849.     }
  850.     *idx = i;
  851. }